#  -*- coding: utf-8 -*-
"""
    xfree.auth.services.registration
    ~~~~~~~~~~~~~~~~~~~~~
    文件描述：实现xfree.core.auth.services中的服务
    作者：xfree
    邮箱：cy_83k@qq.com
"""

from datetime import datetime
from itertools import chain

import attr
from flask import flash
from flask_babelplus import gettext as _
from flask_login import login_user
from pytz import UTC
from sqlalchemy import func

# 从相关模块导入各种注册相关的类和异常
from...core.auth.registration import (
    RegistrationPostProcessor,
    UserRegistrationService,
    UserValidator,
)
from...core.exceptions import (
    PersistenceError,
    StopValidation,
    ValidationError,
)
from...user.models import User

__all__ = (
    "AutoActivateUserPostProcessor",
    "AutologinPostProcessor",
    "EmailUniquenessValidator",
    "RegistrationService",
    "SendActivationPostProcessor",
    "UsernameRequirements",
    "UsernameUniquenessValidator",
    "UsernameValidator",
)


@attr.s(hash=False, repr=True, frozen=True, eq=False, order=False)
class UsernameRequirements(object):
    """
    用户名要求的配置，包括最小和最大长度以及不允许的名称。
    """

    min = attr.ib()
    max = attr.ib()
    blacklist = attr.ib()


class UsernameValidator(UserValidator):
    """
    验证注册用户的用户名是否满足最小要求（合适的长度，不是禁止的名称）。
    """

    def __init__(self, requirements):
        self._requirements = requirements

    def validate(self, user_info):
        """
        执行验证操作。

        :param user_info: 用户注册信息
        :return: 如果用户名不满足要求，抛出ValidationError异常
        """
        if not (
            self._requirements.min <= len(user_info.username) <= self._requirements.max
        ):
            raise ValidationError(
                "username",
                _(
                    "Username must be between %(min)s and %(max)s characters long",
                    min=self._requirements.min,
                    max=self._requirements.max,
                ),
            )

        is_blacklisted = user_info.username in self._requirements.blacklist
        if is_blacklisted:  # pragma: no branch
            raise ValidationError(
                "username",
                _(
                    "%(username)s is a forbidden username",
                    username=user_info.username,
                ),
            )


class UsernameUniquenessValidator(UserValidator):
    """
    验证提供的用户名在应用中是唯一的。
    """

    def __init__(self, users):
        self.users = users

    def validate(self, user_info):
        """
        执行验证操作。

        :param user_info: 用户注册信息
        :return: 如果用户名不唯一，抛出ValidationError异常
        """
        count = self.users.query.filter(
            func.lower(self.users.username) == user_info.username
        ).count()
        if count!= 0:  # pragma: no branch
            raise ValidationError(
                "username",
                _(
                    "%(username)s is already registered",
                    username=user_info.username,
                ),
            )


class EmailUniquenessValidator(UserValidator):
    """
    验证提供的电子邮件在应用中是唯一的。
    """

    def __init__(self, users):
        self.users = users

    def validate(self, user_info):
        """
        执行验证操作。

        :param user_info: 用户注册信息
        :return: 如果电子邮件不唯一，抛出ValidationError异常
        """
        count = self.users.query.filter(
            func.lower(self.users.email) == user_info.email
        ).count()
        if count!= 0:  # pragma: no branch
            raise ValidationError(
                "email",
                _("%(email)s is already registered", email=user_info.email),
            )


class SendActivationPostProcessor(RegistrationPostProcessor):
    """
    注册后发送激活请求。

    :param account_activator: 账户激活器
    :type account_activator: :class:`~xfree.core.auth.activation.AccountActivator`
    """

    def __init__(self, account_activator):
        self.account_activator = account_activator

    def post_process(self, user):
        """
        执行注册后处理操作，发送激活请求并显示提示信息。

        :param user: 新注册的用户
        """
        self.account_activator.initiate_account_activation(user.email)
        flash(
            _(
                "An account activation email has been sent to %(email)s",
                email=user.email,
            ),
            "success",
            )


class AutologinPostProcessor(RegistrationPostProcessor):
    """
    注册后自动登录用户。
    """

    def post_process(self, user):
        """
        执行注册后处理操作，自动登录用户并显示提示信息。

        :param user: 新注册的用户
        """
        login_user(user)
        flash(_("Thanks for registering."), "success")


class AutoActivateUserPostProcessor(RegistrationPostProcessor):
    """
    如果论坛不需要激活，自动将用户标记为已激活。

    :param db: 配置好的Flask - SQLAlchemy扩展对象
    :param config: 当前xfree配置对象
    """

    def __init__(self, db, config):
        self.db = db
        self.config = config

    def post_process(self, user):
        """
        执行注册后处理操作，根据配置决定是否将用户标记为已激活。

        :param user: 新注册的用户
        """
        if not self.config["ACTIVATE_ACCOUNT"]:
            user.activated = True
            self.db.session.commit()


class RegistrationService(UserRegistrationService):
    """
    xfree的默认注册服务，使用提供的验证器对注册信息进行验证，如果验证通过，则创建用户。

    如果任何提供的
    :class:`UserValidators<xfree.core.auth.registration.UserValidator>`
    引发
    :class:`ValidationError<xfree.core.exceptions.ValidationError>`，
    那么register方法将引发一个
    :class:`StopValidation<xfree.core.exceptions.StopValidation>`，
    其中包含所有阻止注册的原因。
    """

    def __init__(self, plugins, users, db):
        self.plugins = plugins
        self.users = users
        self.db = db

    def register(self, user_info):
        """
        注册用户的方法。

        :param user_info: 用户注册信息
        :return: 新注册的用户
        """
        try:
            self._validate_registration(user_info)
        except StopValidation as e:
            self._handle_failure(user_info, e.reasons)
            raise

        user = self._store_user(user_info)
        self._post_process(user)
        return user

    def _validate_registration(self, user_info):
        """
        验证注册信息的方法。

        :param user_info: 用户注册信息
        :return: 如果验证失败，抛出StopValidation异常
        """
        failures = []
        validators = self.plugins.hook.xfree_gather_registration_validators()

        for v in chain.from_iterable(validators):
            try:
                v(user_info)
            except ValidationError as e:
                failures.append((e.attribute, e.reason))
        if failures:
            raise StopValidation(failures)

    def _handle_failure(self, user_info, failures):
        """
        处理注册失败的方法。

        :param user_info: 用户注册信息
        :param failures: 注册失败的原因列表
        """
        self.plugins.hook.xfree_registration_failure_handler(
            user_info=user_info, failures=failures
        )

    def _store_user(self, user_info):
        """
        存储用户信息的方法。

        :param user_info: 用户注册信息
        :return: 新注册的用户
        :raises PersistenceError: 如果无法存储用户信息，抛出此异常
        """
        try:
            user = User(
                username=user_info.username,
                email=user_info.email,
                password=user_info.password,
                language=user_info.language,
                primary_group_id=user_info.group,
                date_joined=datetime.now(UTC),
            )
            self.db.session.add(user)
            self.db.session.commit()
            return user
        except Exception:
            self.db.session.rollback()
            raise PersistenceError("Could not persist user")

    def _post_process(self, user):
        """
        注册后处理的方法。

        :param user: 新注册的用户
        """
        self.plugins.hook.xfree_registration_post_processor(user=user)